
from string import Template

#--------------------------------------------------------------------#
# Events
#--------------------------------------------------------------------#

class Event(object):
  def __init__(self, desc="", team_code=None, player_code=None):
    self.desc = desc
    self.team_code = team_code
    self.player_code = player_code

  def get_string_template(self, detail=True):
    raise NotImplementedError, self.__class__
    #return None

  def get_code(self):
    if self.player_code:
      return self.team_code + self.player_code
    elif self.team_code:
      return self.team_code

  def is_same_player(self, event):
    return (self.player_code == event.player_code and
            self.team_code == event.team_code)

  def is_same_team(self, event):
    return self.team_code == event.team_code

  @staticmethod
  def check(desc):
    raise NotImplementedError, desc

class IgnoredEvent(Event):
  def __init__(self, *args, **kwargs):
    super(IgnoredEvent, self).__init__(*args, **kwargs)

class ActionEvent(Event):
  """Any event that can signal the start of a specific player's action."""
  def __init__(self, *args, **kwargs):
    super(ActionEvent, self).__init__(*args, **kwargs)

class ChainEvent(Event):
  """Any event that forms a chain over subsequent events."""

  # enums
  CHAINED            = 1 # incorporated into this event
  SKIP               = 2 # not incorporated but does not end this chain
  END                = 3 # not incorporated and ends this chain

  def __init__(self, *args, **kwargs):
    super(ChainEvent, self).__init__(*args, **kwargs)

  def chain(self, event):
    for EventClass in (
        ActionEvent,
        ArmorCheckEvent,
        CatchEvent,
        FireballEvent,
        LightningEvent,
        HalfTimeEvent,
        MatchEndEvent,
        SPPEvent,
        TurnoverEvent):
      if (isinstance(event, EventClass)):
        return ChainEvent.END

    if isinstance(event, SkillEvent):
      if event.skill in (
          "Guard",
          "Horns",
          "Jump Up",
          "Multiple Block",
          "No Hands",
          "Shadowing",
          "Stab",
          "Tentacles",
          ):
        return ChainEvent.END

    raise NotImplementedError, event.desc

class RerollChainEvent(ChainEvent):
  """Any event that can be rerolled."""

  def __init__(self, *args, **kwargs):
    super(RerollChainEvent, self).__init__(*args, **kwargs)
    self.reroll = None
    self.rerolled = False
    self.loner = False

  def is_rerolling(self):
    return self.reroll and not self.rerolled and not self.loner

  def chain(self, event):
    # check for loner
    if isinstance(event, LonerEvent):
      assert self.is_same_player(event)
      self.loner = not event.result
      return ChainEvent.CHAINED

    # if loner is set and failed, defer to loner
    if isinstance(event, SkillEvent):
      if event.skill == "Loner":
        assert self.is_same_player(event)
        assert self.loner == True
        return ChainEvent.CHAINED

      if event.skill == "Leader":
        self.reroll = event
        return ChainEvent.CHAINED

    # check for reroll
    if isinstance(event, RerollEvent):
      assert self.is_same_player(event)
      self.reroll = event
      return ChainEvent.CHAINED

    return super(RerollChainEvent, self).chain(event)

class InjuryChainEvent(ChainEvent):
  """Any event that forms a chain over subsequent events possibly ending in
     injury."""

  def __init__(self, *args, **kwargs):
    super(InjuryChainEvent, self).__init__(*args, **kwargs)
    self.injury_check = None
    self.casualty_checks = [] # could be multiple for decay
    self.apoth = None

  def chain(self, event):
    if isinstance(event, InjuryCheckEvent):
      assert not self.injury_check
      self.injury_check = event
      return ChainEvent.CHAINED

    if isinstance(event, CasualtyCheckEvent):
      assert self.injury_check.result == InjuryCheckEvent.CASUALTY
      if not self.apoth:
        self.casualty_checks.append(event)
        return ChainEvent.CHAINED
      else:
        # check for apoth casualty result; assume the minimum casualty is picked
        assert len(self.casualty_checks) == 1
        casualty_check = self.casualty_checks[0]
        assert casualty_check.is_same_player(event)
        casualty_check.result = min(casualty_check.result, event.result)
        return ChainEvent.CHAINED

    if isinstance(event, SkillEvent):
      if event.skill == "Thick Skull":
        assert self.injury_check.result == InjuryCheckEvent.KO
        self.injury_check.result = InjuryCheckEvent.STUN
        return ChainEvent.CHAINED

      if event.skill == "Stunty":
        if self.injury_check.result == InjuryCheckEvent.STUN:
          self.injury_check.result = InjuryCheckEvent.KO
        elif self.injury_check.result == InjuryCheckEvent.KO:
          self.injury_check.result = InjuryCheckEvent.CASUALTY
        else:
          raise NotImplementedError
        return ChainEvent.CHAINED

    if isinstance(event, ApothEvent):
      self.apoth = event
      return ChainEvent.CHAINED

    if isinstance(event, RegenEvent):
      if event.result:
        self.apoth = event
      return ChainEvent.CHAINED

    return super(InjuryChainEvent, self).chain(event)

  def get_injury_name(self):
    if self.injury_check.result == InjuryCheckEvent.CASUALTY:
      return " and ".join(check.casualty_desc for check in self.casualty_checks)
    elif self.injury_check.result == InjuryCheckEvent.KO:
      return "is knocked out"
    else:
      assert self.injury_check.result == InjuryCheckEvent.STUN
      return "is stunned"

# -1 Code
#--------------------------------------------------------------------#

class NewMatchEvent(IgnoredEvent):
  @staticmethod
  def check(desc):
    return not desc.strip()

class TurnoverEvent(Event):
  def __init__(self, desc):
    team_code = desc[0:3]
    super(TurnoverEvent, self).__init__(desc, team_code)

  def get_string_template(self, detail=True):
    return Template("${%s} suffer a turnover!" % self.get_code())

  @staticmethod
  def check(desc):
    return "suffer a TURNOVER!" in desc

# 00 Code
#--------------------------------------------------------------------#

class BlitzEvent(ActionEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(BlitzEvent, self).__init__(desc, team_code, player_code)

  def get_string_template(self, detail=True):
    return Template("${%s} starts ${%s_ogp} blitz!" % (
      self.get_code(), self.get_code()))

  @staticmethod
  def check(desc):
    return desc.endswith("Blitz!")

# 02 Code
#--------------------------------------------------------------------#

class AlwaysHungryEvent(RerollChainEvent, ActionEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(AlwaysHungryEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")

  def get_string_template(self, detail=True):
    if not self.result:
      return Template("${%s} is hungry!" % self.get_code())

  @staticmethod
  def check(desc):
    return " Always Hungry  (2+) :" in desc

  def chain(self, event):
    # check for second attempt
    if isinstance(event, AlwaysHungryEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    return super(AlwaysHungryEvent, self).chain(event)

class ArmorCheckEvent(InjuryChainEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(ArmorCheckEvent, self).__init__(desc, team_code, player_code)

    self.ejection = False
    self.bribe = True
    self.result = desc.endswith("Success")

  @staticmethod
  def check(desc):
    return " Armour Value  " in desc

  def chain(self, event):
    if isinstance(event, BribeEvent):
      self.bribe = True
      return ChainEvent.CHAINED

    if isinstance(event, EjectionEvent):
      self.ejection = True
      return ChainEvent.CHAINED

    return super(ArmorCheckEvent, self).chain(event)

class BlockEvent(InjuryChainEvent, RerollChainEvent, ActionEvent):

  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(BlockEvent, self).__init__(desc, team_code, player_code)

    self.catch = None
    self.choice = None
    self.crowd_push = False
    self.my_armor_check = None
    self.op_armor_check = None
    self.strip = False # TODO
    self.unknown_armor_check = None

  def get_string_template(self, detail=True):
    if self.crowd_push:
      assert self.choice.result in (
        BlockChoiceEvent.DEFENDER_DOWN,
        BlockChoiceEvent.DEFENDER_STUMBLES,
        BlockChoiceEvent.PUSHED,
        BlockChoiceEvent.DEFENDER_DODGES)
      assert not self.my_armor_check and not self.op_armor_check

      return Template("${%s} pushes ${%s}, who is mobbed by the crowd and %s!" % (
        self.get_code(), self.injury_check.get_code(), self.get_injury_name()))

    if self.choice.result in (
        BlockChoiceEvent.ATTACKER_DOWN,
        BlockChoiceEvent.BOTH_DOWN_DEF_BLOCK):
      text = "${%s} attempts a block but is overpowered by ${%s_ogp} opponent" \
          % (self.get_code(), self.get_code())

      if self.my_armor_check and self.my_armor_check.result:
        text += " and %s!" % (
            self.my_armor_check.get_injury_name())
      return Template(text)

    if self.choice.result in (
        BlockChoiceEvent.BOTH_DOWN_ATT_BLOCK,
        BlockChoiceEvent.DEFENDER_DOWN,
        BlockChoiceEvent.DEFENDER_STUMBLES):
      text = "${%s} blocks ${%s}, who hits the ground" % (
          self.get_code(), self.op_armor_check.get_code())

      if self.op_armor_check.result:
        text += " and %s!" % self.op_armor_check.get_injury_name()
      return Template(text)

    if detail and self.choice.result == BlockChoiceEvent.PUSHED:
      return Template("${%s} pushes ${%s_ogp} opponent" % (
        self.get_code(), self.get_code()))

    if detail and self.choice.result == BlockChoiceEvent.DEFENDER_DODGES:
      return Template("${%s}'s opponent dodges out of a block attempt" % (
        self.get_code()))

    if detail and self.choice.result == BlockChoiceEvent.DEFENDER_TACKLES:
      return Template("${%s} tackles ${%s_ogp} opponent to the ground" % (
        self.get_code(), self.get_code()))

    if detail and self.choice.result == BlockChoiceEvent.BOTH_DOWN_BOTH_BLOCK:
      return Template("${%s}'s opponent holds ground on the block attempt" % (
        self.get_code()))

  @staticmethod
  def check(desc):
    return " Block  Result:" in desc

  def chain(self, event):

    # check for block choice
    if isinstance(event, BlockChoiceEvent):
      assert self.is_same_player(event)
      self.choice = event
      return ChainEvent.CHAINED

    # check for second attempt (no action in this case)
    if isinstance(event, BlockEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      return ChainEvent.CHAINED

    # check for skills
    if isinstance(event, SkillEvent):
      if event.skill == "Block":
        if event.team_code == self.team_code:
          assert self.is_same_player(event)
          map = {
              BlockChoiceEvent.BOTH_DOWN_NO_BLOCK:  BlockChoiceEvent.BOTH_DOWN_ATT_BLOCK,
              BlockChoiceEvent.BOTH_DOWN_DEF_BLOCK: BlockChoiceEvent.BOTH_DOWN_BOTH_BLOCK}
        else:
          map = {
              BlockChoiceEvent.BOTH_DOWN_NO_BLOCK:  BlockChoiceEvent.BOTH_DOWN_DEF_BLOCK,
              BlockChoiceEvent.BOTH_DOWN_ATT_BLOCK: BlockChoiceEvent.BOTH_DOWN_BOTH_BLOCK}

        self.choice.result = map[self.choice.result]
        return ChainEvent.CHAINED

      if event.skill == "Claw":
        return ChainEvent.CHAINED

      if event.skill == "Dodge":
        assert self.choice.result == BlockChoiceEvent.DEFENDER_STUMBLES
        assert not self.is_same_player(event)
        self.choice.result = BlockChoiceEvent.DEFENDER_DODGES
        return ChainEvent.CHAINED

      if event.skill == "Fend":
        return ChainEvent.CHAINED

      if event.skill == "Frenzy":
        assert self.is_same_player(event)
        # treat this as a second block and minimize the headache
        return ChainEvent.END

      if event.skill == "Piling On":
        # this one is really tricky to handle -- we do our best with the armor
        # check above and in the InjuryChainEvent
        assert self.is_same_player(event)
        if self.unknown_armor_check:
          # update the result and reset the unknown_armor_check
          self.op_armor_check.result = self.unknown_armor_check.result
          self.unknown_armor_check = None
        return ChainEvent.CHAINED

      if event.skill == "Side Step":
        assert not self.is_same_player(event)
        return ChainEvent.CHAINED

      if event.skill == "Stand Firm":
        assert not self.is_same_player(event)
        if self.choice.result in (
            BlockChoiceEvent.PUSHED,
            BlockChoiceEvent.DEFENDER_DODGES):
          self.choice.result == BlockChoiceEvent.BOTH_DOWN_BOTH_BLOCK
        return ChainEvent.CHAINED

      if event.skill == "Strip Ball":
        self.strip = True
        return ChainEvent.CHAINED

      if event.skill == "Tackle":
        assert self.choice.result == BlockChoiceEvent.DEFENDER_STUMBLES
        self.choice.result = BlockChoiceEvent.DEFENDER_TACKED
        return ChainEvent.CHAINED

      if event.skill == "Wrestle":
        self.choice.result = BlockChoiceEvent.BOTH_DOWN_WRESTLE
        return ChainEvent.CHAINED

    # check for catch somewhere in the middle
    if isinstance(event, CatchEvent):
      self.catch = event
      return ChainEvent.CHAINED, event

    if self.catch:
      ret_val = self.catch.chain(event)
      if ret_val != ChainEvent.END:
        return ret_val
      self.catch = None

    # check for armor rolls
    if isinstance(event, ArmorCheckEvent):
      if self.is_same_player(event):
        if not self.my_armor_check and (
            self.choice.result <= BlockChoiceEvent.BOTH_DOWN_DEF_BLOCK):
          self.my_armor_check = event
          return ChainEvent.CHAINED

      else:
        if not self.op_armor_check and (
            self.choice.result >= BlockChoiceEvent.BOTH_DOWN_ATT_BLOCK or
            self.choice.result == BlockChoiceEvent.BOTH_DOWN_NO_BLOCK):
          self.op_armor_check = event
          return ChainEvent.CHAINED

        elif (self.op_armor_check and
            self.op_armor_check.is_same_player(event) and
            not self.op_armor_check.injury_check):
          # could be either a foul or a second piling on armor check.
          # assume it's piling on for now and we'll back-track if we're wrong.
          self.unknown_armor_check = event
          return ChainEvent.CHAINED

    # check for piling on injury check
    if (isinstance(event, InjuryCheckEvent) and
        self.op_armor_check and
        self.op_armor_check.injury_check and
        self.op_armor_check.injury_check.is_same_player(event)):
      assert not self.unknown_armor_check
      self.op_armor_check.injury_check.result = event.result
      return ChainEvent.CHAINED

    # Check for crowd push, which is what makes this an injury chain event.
    # This is tricky -- we also need to allow for a chain crowd push after a
    # knock down (!)
    if (isinstance(event, InjuryCheckEvent) and not self.my_armor_check and
        (not self.op_armor_check or not
          self.op_armor_check.is_same_player(event))):
      assert self.choice.result >= BlockChoiceEvent.PUSHED
      self.crowd_push = True
      return super(BlockEvent, self).chain(event)

    # if we have seen an armor roll, and it broke, tentatively chain to the
    # armor check
    armor_check = (self.op_armor_check, self.my_armor_check)[
        self.is_same_player(event)]
    if armor_check and armor_check.result:
      ret_val = armor_check.chain(event)
      if ret_val != ChainEvent.END:
        return ret_val

    if isinstance(event, EjectionEvent):
      assert self.unknown_armor_check
      return ChainEvent.END, self.unknown_armor_check

    ret_val = super(BlockEvent, self).chain(event)
    if ret_val != ChainEvent.END:
      return ret_val

    # we would normally end here, but first check to see if we saw a double
    # armor check and we didn't see piling on, if so, then we return
    # the unknown armor armor check which must have been a foul.
    if self.unknown_armor_check:
      return ChainEvent.END, self.unknown_armor_check
    else:
      return ret_val

class BlockChoiceEvent(Event):
  # result enums
  ATTACKER_DOWN        = 1
  BOTH_DOWN_NO_BLOCK   = 2
  BOTH_DOWN_DEF_BLOCK  = 3
  BOTH_DOWN_BOTH_BLOCK = 4
  BOTH_DOWN_WRESTLE    = 5
  PUSHED               = 6
  DEFENDER_DODGES      = 7
  BOTH_DOWN_ATT_BLOCK  = 8
  DEFENDER_TACKED      = 9
  DEFENDER_STUMBLES    = 10
  DEFENDER_DOWN        = 11

  block_result_map = {
      "Defender Down":     DEFENDER_DOWN,
      "Defender Stumbles": DEFENDER_STUMBLES,
      "Pushed":            PUSHED,
      "Both Down":         BOTH_DOWN_NO_BLOCK,
      "Attacker Down":     ATTACKER_DOWN}

  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(BlockChoiceEvent, self).__init__(desc, team_code, player_code)

    block_result_string = desc.split(":")[-1].strip()
    self.result = BlockChoiceEvent.block_result_map[block_result_string]

  @staticmethod
  def check(desc):
    return " chooses : " in desc

class BloodLustEvent(InjuryChainEvent, RerollChainEvent, ActionEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(BloodLustEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")

  def get_string_template(self, detail=True):
    if detail and not self.result:
      return Template("${%s} yearns for blood" % self.get_code())

  @staticmethod
  def check(desc):
    return " Blood Lust  (2+) :" in desc

  def chain(self, event):
    if self.result:
      return ChainEvent.END

    # check for second attempt
    if isinstance(event, BloodLustEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    if isinstance(event, SkillEvent):
      if event.skill == "Blood Lust":
        return ChainEvent.CHAINED

    return super(BloodLustEvent, self).chain(event)

class BombEvent(InjuryChainEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(BombEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")

  def get_string_template(self, detail=True):
    if self.result:
      return Template("${%s} is knocked down by a thrown bomb!" % self.get_code())
    else:
      return Template("${%s} evades a throws bomb!" % self.get_code())

  @staticmethod
  def check(desc):
    return " Bomb (4+) :" in desc

  def chain(self, event):
    if not self.result:
      return ChainEvent.END

    return super(BombEvent, self).chain(event)

class BoneHeadEvent(RerollChainEvent, ActionEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(BoneHeadEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")

  def get_string_template(self, detail=True):
    if detail and not self.result:
      return Template("${%s} stares around stupidly" % self.get_code())

  @staticmethod
  def check(desc):
    return " Bone-head  (2+) :" in desc

  def chain(self, event):
    # check for second attempt
    if isinstance(event, BoneHeadEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    return super(BoneHeadEvent, self).chain(event)

class BounceEvent(IgnoredEvent):
  @staticmethod
  def check(desc):
    return desc.startswith("Bounce (D8)")

class CasualtyCheckEvent(Event):
  OUT_FOR_MATCH  = 1
  OUT_NEXT_MATCH = 2
  NIGGLE         = 3
  MINUS_MA       = 4
  MINUS_AV       = 5
  MINUS_AG       = 6
  MINUS_ST       = 7
  DEAD           = 8

  casualty_result_map = {
      "Badly Hurt":     (OUT_FOR_MATCH, "is badly hurt"),
      "Broken Ribs":    (OUT_FOR_MATCH, "suffers a broken rib (MNG)"),
      "Groin Strain":   (OUT_FOR_MATCH, "suffers a groin strain (MNG)"),
      "Gouged Eye":     (OUT_FOR_MATCH, "suffers a gouged eye (MNG)"),
      "Broken Jaw":     (OUT_FOR_MATCH, "suffers a broken jaw (MNG)"),
      "Fractured Arm":  (OUT_FOR_MATCH, "suffers a fractured arm (MNG)"),
      "Fractured Leg":  (OUT_FOR_MATCH, "suffers a fractured leg (MNG)"),
      "Smashed Hand":   (OUT_FOR_MATCH, "suffers a smashed hand (MNG)"),
      "Pinched Nerve":  (OUT_FOR_MATCH, "suffers a pinched nerve (MNG)"),
      "Damaged Back":        (NIGGLE,   "suffers a damanged back (Niggling injury)"),
      "Smashed Knee":        (NIGGLE,   "suffers a smashed knee (Niggling injury)"),
      "Smashed Hip":         (MINUS_MA, "suffers a smashed hip (-1 MA)"),
      "Smashed Ankle":       (MINUS_MA, "suffers a smashed ankle (-1 MA)"),
      "Serious Concussion":  (MINUS_AV, "suffers a serious concussion (-1 AV)"),
      "Fractured Skull":     (MINUS_AV, "suffers a fractured skull (-1 AV)"),
      "Broken neck":         (MINUS_AG, "suffers a broken neck (-1 AG)"),
      "Smashed Collar Bone": (MINUS_ST, "suffers a smashed collar bone (-1 ST)"),
      "Dead":                (DEAD,     "suffers a fatal injury (DEAD)")}

  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(CasualtyCheckEvent, self).__init__(desc, team_code, player_code)

    casualty_string = desc.split(":")[-1].split("->")[0].strip()
    self.result, self.casualty_desc = (
        CasualtyCheckEvent.casualty_result_map[casualty_string])

  @staticmethod
  def check(desc):
    return " Casualty  :" in desc

class CatchEvent(RerollChainEvent): # not an action event
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(CatchEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")

    self.handoff = "{Accurate Pass}" in desc
    self.bouncing = "{Bouncing Ball}" in desc
    self.interception = False
    self.safe_throw = None

  def get_string_template(self, detail=True):
    assert self.interception or self.accurate 
    if self.result:
      return Template("${%s} makes an interception!" % self.get_code())

  @staticmethod
  def check(desc):
    return " Catch {AG} " in desc

  def chain(self, event):
    # check for skills
    if isinstance(event, SkillEvent):
      if event.skill == "Catch":
        assert self.is_same_player(event) and not self.reroll
        self.reroll = event
        return ChainEvent.CHAINED

      if event.skill == "Disturbing Presence":
        return ChainEvent.CHAINED

    # check for second attempt
    if isinstance(event, CatchEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    # this could be an interception attempt -- check for safe throw
    if isinstance(event, SafeThrowEvent):
      self.safe_throw = event
      if self.safe_throw.result:
        self.result = False
      return ChainEvent.CHAINED

    if self.safe_throw:
      ret_val = self.safe_throw.chain(event)

      # re-check the safe throw result
      if self.safe_throw.result:
        self.result = False

      if ret_val != ChainEvent.END:
        return ret_val

    # check for interception
    if isinstance(event, SPPEvent) and event.achievement == "Interception":
      self.interception = True
      return ChainEvent.CHAINED

    return super(CatchEvent, self).chain(event)

class ChainsawEvent(RerollChainEvent, ActionEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(ChainsawEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")
    self.armor_check = None

  def get_string_template(self, detail=True):

    if self.result:
      if self.armor_check.result:
        return Template("${%s} tears ${%s_ogp} chainsaw into c${%s}, who %s!" % (
          self.get_code(), self.armor_check.get_code(),
          self.armor_check.get_injury_name()))
      elif detail:
        return Template("${%s} uses ${%s_ogp} chainsaw to knock down ${%s}" % (
          self.get_code(), self.get_code(), self.armor_check.get_code()))

    else:
      if self.armor_check.result:
        return Template("${%s}'s chainsaw kicks back and ${%s_sgp} %s!" % (
          self.armor_check.get_code(), self.get_code(),
          self.armor_check.get_injury_name()))
      elif detail:
        return Template("${%s} is knocked down by {%s_ogp} own chainsaw" % (
          self.armor_check.get_code(), self.get_code()))

  @staticmethod
  def check(desc):
    return " Chainsaw  (2+) :" in desc

  def chain(self, event):

    # check for second attempt
    if isinstance(event, ChainsawEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    # if we have seen the armor roll, chain to it
    if self.armor_check:
      return self.armor_check.chain(event)

    # check for an armor roll
    if isinstance(event, ArmorCheckEvent):
      self.armor_check = event
      return ChainEvent.CHAINED

    return super(ChainsawEvent, self).chain(event)

class DauntlessEvent(RerollChainEvent, ActionEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(DauntlessEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")

  def get_string_template(self, detail=True):
    if detail and self.result:
      return Template("${%s} feels dauntless" % self.get_code())

  @staticmethod
  def check(desc):
    return " Dauntless  (" in desc

  def chain(self, event):
    if self.result:
      return ChainEvent.END

    # check for second attempt
    if isinstance(event, DauntlessEvent) and self.is_rerolling():
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    if isinstance(event, SkillEvent):
      if event.skill == "Dauntless":
        return ChainEvent.CHAINED

    return super(DauntlessEvent, self).chain(event)

class DodgeEvent(RerollChainEvent, ActionEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(DodgeEvent, self).__init__(desc, team_code, player_code)

    self.armor_check = None
    self.result = desc.endswith("Success")
    
  def get_string_template(self, detail=True):
    if self.result:
      return Template("${%s} makes a dodge" % self.get_code())
    else:
      return Template("${%s} fails a dodge!" % self.get_code())

  @staticmethod
  def check(desc):
    return " Dodge {AG} " in desc or " Dodge {ST} " in desc

  def chain(self, event):
    # if we succeeded then the event is already over
    if self.result:
      return ChainEvent.END

    # check for skills
    if isinstance(event, SkillEvent):
      if event.skill == "Dodge":
        assert self.is_same_player(event) and not self.reroll
        self.reroll = event
        return ChainEvent.CHAINED

      if event.skill == "Prehensile Tail":
        return ChainEvent.CHAINED

      if event.skill == "Diving Tackle":
        #TODO
        return ChainEvent.CHAINED

    # check for second attempt
    if isinstance(event, DodgeEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    # if we have seen the armor roll, chain to it
    if self.armor_check:
      return self.armor_check.chain(event)

    # check for an armor roll
    if isinstance(event, ArmorCheckEvent):
      assert self.is_same_player(event)
      self.armor_check = event
      return ChainEvent.CHAINED

    return super(DodgeEvent, self).chain(event)

class EjectionEvent(Event):
  def __init__(self, desc):
    team_code = desc[0:3]
    super(EjectionEvent, self).__init__(desc, team_code)

  @staticmethod
  def check(desc):
    return " is a double -> " in desc

class FireballEvent(InjuryChainEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(FireballEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")
    self.armor_check = None

  def get_string_template(self, detail=True):
    if self.result:
      return Template("${%s} is knocked down by a fireball!" % self.get_code())
    else:
      return Template("${%s} evades a fireball" % self.get_code())

  @staticmethod
  def check(desc):
    return " Fireball (4+) :" in desc

  def chain(self, event):
    if not self.result:
      return ChainEvent.END

    if isinstance(event, ApothEvent):
      # this is too hard to track -- I won't even try
      return ChainEvent.END

    # if we have seen the armor roll, chain to it
    if self.armor_check:
      return self.armor_check.chain(event)

    # check for an armor roll
    if isinstance(event, ArmorCheckEvent):
      assert self.is_same_player(event)
      self.armor_check = event
      return ChainEvent.CHAINED

    return super(FireballEvent, self).chain(event)

class GFIEvent(RerollChainEvent, ActionEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(GFIEvent, self).__init__(desc, team_code, player_code)

    self.armor_check = None
    self.result = desc.endswith("Success")

  def get_string_template(self, detail=True):
    if self.result:
      return Template("${%s} goes for it and makes it" % self.get_code())
    else:
      return Template("${%s} goes for it and trips!" % self.get_code())

  @staticmethod
  def check(desc):
    return " Going for it " in desc

  def chain(self, event):
    # if we succeeded then the event is already over
    if self.result:
      return ChainEvent.END

    # check for skills
    if isinstance(event, SkillEvent):
      if event.skill == "Sure Feet":
        assert self.is_same_player(event) and not self.reroll
        self.reroll = event
        return ChainEvent.CHAINED

    # check for second attempt
    if isinstance(event, GFIEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    # if we have seen the armor roll, chain to it
    if self.armor_check:
      return self.armor_check.chain(event)

    # check for an armor roll
    if isinstance(event, ArmorCheckEvent):
      assert self.is_same_player(event)
      self.armor_check = event
      return ChainEvent.CHAINED

    return super(GFIEvent, self).chain(event)

class FoulAppearanceEvent(RerollChainEvent, ActionEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(FoulAppearanceEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")
    self.use    = None

  def get_string_template(self, detail=True):
    if detail and not self.result:
      return Template("${%s} is too disgusted by ${%s} to block ${%s_ogp}" % (
        self.get_code(), self.use.get_code(), self.use.get_code()))

  @staticmethod
  def check(desc):
    return " Foul Appearance  (" in desc

  def chain(self, event):
    # if we succeeded then the event is already over
    if self.result:
      return ChainEvent.END

    # check for second attempt
    if isinstance(event, FoulAppearanceEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    if isinstance(event, SkillEvent):
      if event.skill == "Foul Appearance":
        self.use = event
        return ChainEvent.CHAINED

    return super(FoulAppearanceEvent, self).chain(event)

class HypnoticGazeEvent(RerollChainEvent, ActionEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(HypnoticGazeEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")

  def get_string_template(self, detail=True):
    if self.result:
      return Template("${%s} aches for blood" % self.get_code())
    else:
      return Template("${%s} hypnotizes his opponent" % self.get_code())

  @staticmethod
  def check(desc):
    return " Hypnotic Gaze {AG} " in desc

  def chain(self, event):
    # check for second attempt
    if isinstance(event, HypnoticGazeEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    if isinstance(event, SkillEvent):
      if event.skill == "Hypnotic Gaze":
        return ChainEvent.CHAINED

    return super(HypnoticGazeEvent, self).chain(event)

class InjuryCheckEvent(Event):
  STUN     = 0
  KO       = 1
  CASUALTY = 2

  injury_result_map = {
      "Stunned": STUN,
      "KO'd":    KO,
      "Injured": CASUALTY}

  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(InjuryCheckEvent, self).__init__(desc, team_code, player_code)

    injury_string = desc.split("->")[-1].strip()
    self.result = InjuryCheckEvent.injury_result_map[injury_string]

  @staticmethod
  def check(desc):
    return " Injury  :" in desc

class JumpUpEvent(IgnoredEvent):
  @staticmethod
  def check(desc):
    return " Jump Up {AG} " in desc

class KickEvent(Event):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(KickEvent, self).__init__(desc, team_code, player_code)

  def get_string_template(self, detail=True):
    if detail:
      return Template("${%s} takes the kick" % self.get_code())

  @staticmethod
  def check(desc):
    return " Kick-off Direction (D8) :" in desc

class KickDistanceEvent(IgnoredEvent):
  @staticmethod
  def check(desc):
    return " Kick-off Distance (D6) :" in desc

class KORecoverEvent(IgnoredEvent):
  @staticmethod
  def check(desc):
    # TODO: handle bloodweiser babes
    return "KO (4+) :" in desc

class LandEvent(RerollChainEvent, ActionEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(LandEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")

  @staticmethod
  def check(desc):
    return " Land  (4+) :" in desc

  def chain(self, event):
    # check for second attempt
    if isinstance(event, LandEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    return super(LandEvent, self).chain(event)

class LeapEvent(RerollChainEvent, ActionEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(LeapEvent, self).__init__(desc, team_code, player_code)

    self.armor_check = None
    self.result = desc.endswith("Success")
    
  def get_string_template(self, detail=True):
    if self.result:
      return Template("${%s} makes a leap" % self.get_code())
    else:
      return Template("${%s} fails a leap!" % self.get_code())

  @staticmethod
  def check(desc):
    return " Leap  (" in desc

  def chain(self, event):
    if self.result:
      return ChainEvent.END

    # check for skills
    if isinstance(event, SkillEvent):
      if event.skill == "Leap":
        return ChainEvent.CHAINED

    # check for second attempt
    if isinstance(event, LeapEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    # if we have seen the armor roll, chain to it
    if self.armor_check:
      return self.armor_check.chain(event)

    # check for an armor roll
    if isinstance(event, ArmorCheckEvent):
      assert self.is_same_player(event)
      self.armor_check = event
      return ChainEvent.CHAINED

    return super(LeapEvent, self).chain(event)

class LightningEvent(InjuryChainEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(LightningEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")
    self.armor_check = None

  def get_string_template(self, detail=True):
    if self.result:
      return Template("${%s} is knocked down by a lightning bolt!" % self.get_code())
    else:
      return Template("${%s} evades a lightning bolt" % self.get_code())

  @staticmethod
  def check(desc):
    return " Lightning Bolt (2+) :" in desc

  def chain(self, event):
    if not self.result:
      return ChainEvent.END

    if isinstance(event, ApothEvent):
      # this is too hard to track -- I won't even try
      return ChainEvent.END

    # if we have seen the armor roll, chain to it
    if self.armor_check:
      return self.armor_check.chain(event)

    # check for an armor roll
    if isinstance(event, ArmorCheckEvent):
      assert self.is_same_player(event)
      self.armor_check = event
      return ChainEvent.CHAINED

    return super(LightningEvent, self).chain(event)

class LonerEvent(Event):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(LonerEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")

  @staticmethod
  def check(desc):
    return " Loner (4+) :" in desc

class PickUpEvent(RerollChainEvent, ActionEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(PickUpEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")
    self.reroll = None

  def get_string_template(self, detail=True):
    if self.result:
      return Template("${%s} scoops up the ball" % self.get_code())
    else:
      return Template("${%s} fails to pick up the ball!" % self.get_code())

  @staticmethod
  def check(desc):
    return " Pick-up {AG} " in desc

  def chain(self, event):
    # check for skills
    if isinstance(event, SkillEvent):
      if event.skill == "Sure Hands":
        assert self.is_same_player(event) and not self.reroll
        self.reroll = event
        return ChainEvent.CHAINED

    # check for second attempt
    if isinstance(event, PickUpEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    return super(PickUpEvent, self).chain(event)

class RegenEvent(Event):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(RegenEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")

  @staticmethod
  def check(desc):
    return " Regeneration (4+) :" in desc

class ReallyStupidEvent(RerollChainEvent, ActionEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(ReallyStupidEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")

  def get_string_template(self, detail=True):
    if detail and not self.result:
      return Template("${%s} stares around stupidly" % self.get_code())

  @staticmethod
  def check(desc):
    return " Really Stupid  (4+) :" in desc

  def chain(self, event):
    # check for second attempt
    if isinstance(event, ReallyStupidEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    return super(ReallyStupidEvent, self).chain(event)

class StandUpEvent(RerollChainEvent, ActionEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(StandUpEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")

  def get_string_template(self, detail=True):
    if detail:
      if self.result:
        return Template("${%s} successfully stands up" % self.get_code())
      else:
        return Template("${%s} fails to stand up" % self.get_code())

  @staticmethod
  def check(desc):
    return " Stand-up  (4+) :" in desc

  def chain(self, event):
    # check for second attempt
    if isinstance(event, StandUpEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    return super(StandUpEvent, self).chain(event)

class TakeRootEvent(RerollChainEvent, ActionEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(TakeRootEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")

  def get_string_template(self, detail=True):
    if detail and not self.result:
      return Template("${%s} digs his root into the ground" % self.get_code())

  @staticmethod
  def check(desc):
    return " Take Root  (2+) :" in desc

  def chain(self, event):
    # check for second attempt
    if isinstance(event, TakeRootEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    if isinstance(event, SkillEvent):
      if event.skill == "Take Root":
        return ChainEvent.CHAINED

    return super(TakeRootEvent, self).chain(event)

class SafeThrowEvent(RerollChainEvent, ActionEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(SafeThrowEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")

  def get_string_template(self, detail=True):
    if detail and self.result:
      return Template("${%s} throws safely" % self.get_code())

  @staticmethod
  def check(desc):
    return " Safe Throw {AG}  " in desc

  def chain(self, event):
    # check for second attempt
    if isinstance(event, SafeThrowEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    return super(SafeThrowEvent, self).chain(event)

class ScatterEvent(IgnoredEvent):
  @staticmethod
  def check(desc):
    return desc.startswith("Scatter Launch (D8)")

class ShadowingEvent(RerollChainEvent):
  def __init__(self, desc):
    team_code = desc[0:3]
    player_code = desc.split()[5][1:]
    super(ShadowingEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")

  @staticmethod
  def check(desc):
    return " score : " in desc

  def chain(self, event):
    # check for second attempt
    if isinstance(event, ShadowingEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    return super(ShadowingEvent, self).chain(event)

class SwelteringHeatEvent(IgnoredEvent):
  @staticmethod
  def check(desc):
    return "Sweltering Heat (2+) :" in desc

class ThrowEvent(InjuryChainEvent, RerollChainEvent, ActionEvent):
  FUMBLE = 0
  INACCURATE = 1
  ACCURATE = 2

  throw_result_map = {
      "Fumble!":          FUMBLE,
      "Inaccurate pass!": INACCURATE,
      "Accurate pass!":   ACCURATE}

  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(ThrowEvent, self).__init__(desc, team_code, player_code)

    throw_string = desc.split("->")[-1].strip()
    self.result = ThrowEvent.throw_result_map[throw_string]

    self.bomb_toss = None
    self.catch = None

  def get_string_template(self, detail=True):
    if self.bomb_toss or self.injury_check:
      # TODO
      return Template("${%s} throws a bomb!" % self.get_code())

    # TODO: handle catch in the wrong hands
    if self.result == ThrowEvent.FUMBLE:
      return Template("${%s} fumbles a pass attempt!" % self.get_code())
    if self.result == ThrowEvent.INACCURATE:
      if self.catch:
        if self.catch.result:
          return Template("${%s}'s inaccurate pass is caught by ${%s}!" % (
            self.get_code(), self.catch.get_code()))
        else:
          return Template("${%s}'s inaccurate pass is dropped by ${%s}!" % (
            self.get_code(), self.catch.get_code()))
      else:
        return Template("${%s}'s pass misses its target!" %
          self.get_code())

    if self.result == ThrowEvent.ACCURATE:
      if self.catch.result:
        return Template("${%s}'s accurate pass is caught by ${%s}!" % (
          self.get_code(), self.catch.get_code()))
      else:
        return Template("${%s}'s accurate pass is dropped by ${%s}!" % (
          self.get_code(), self.catch.get_code()))

  @staticmethod
  def check(desc):
    return " Launch {AG} " in desc

  def chain(self, event):
    # check for skills
    if isinstance(event, SkillEvent):
      if event.skill == "Pass":
        assert self.is_same_player(event) and not self.reroll
        self.reroll = event
        return ChainEvent.CHAINED

      if event.skill == "Disturbing Presence":
        return ChainEvent.CHAINED

      if event.skill == "Safe Throw":
        return ChainEvent.CHAINED

    # check for second attempt
    if isinstance(event, ThrowEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    # check for catch
    if isinstance(event, CatchEvent):
      self.catch = event
      return ChainEvent.CHAINED

    if self.catch:
      ret_val = self.catch.chain(event)
      if ret_val != ChainEvent.END:
        return ret_val

    if isinstance(event, BombEvent):
      self.bomb_toss = True
      return ChainEvent.END

    return super(ThrowEvent, self).chain(event)

class TentaclesEvent(Event):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(TentaclesEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")

  def get_string_template(self, detail=True):
    if detail and self.result:
      return Template("${%s} avoids a tentacle" % self.get_code())

    if not self.result:
      return Template("${%s} is caught by a tentacle" % self.get_code())

  @staticmethod
  def check(desc):
    return " Tentacles (6+) :" in desc

class ThrowInDirectionEvent(IgnoredEvent):
  @staticmethod
  def check(desc):
    return desc.startswith("Throw-in Direction (D6)")

class ThrowInDistanceEvent(IgnoredEvent):
  @staticmethod
  def check(desc):
    return desc.startswith("Throw-in Distance (2D6)")

class ThrowTeamMateEvent(RerollChainEvent, ActionEvent):
  FUMBLE = 0
  INACCURATE = 1

  throw_result_map = {
      "Fumble!":          FUMBLE,
      "Inaccurate pass!": INACCURATE,
      }

  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(ThrowTeamMateEvent, self).__init__(desc, team_code, player_code)

    throw_string = desc.split("->")[-1].strip()
    self.result = ThrowTeamMateEvent.throw_result_map[throw_string]
    self.landing = None

  def get_string_template(self, detail=True):
    if self.result == ThrowTeamMateEvent.FUMBLE:
      return Template("${%s} fumbles the toss of ${%s_ogp} team mate!" %
          (self.get_code(), self.get_code()))
    elif self.result == ThrowTeamMateEvent.INACCURATE:
      text = "${%s} successfully tosses ${%s_ogp} team mate" % (
          self.get_code(), self.get_code())
      if self.landing.result:
        text += ", who makes a perfect landing!"
      else:
        text += ", who lands flat on his face!"
      return Template(text)
    else:
      raise NotImplementedError

  @staticmethod
  def check(desc):
    return " Throw Team-Mate {AG} " in desc

  def chain(self, event):
    # check for skills
    if isinstance(event, SkillEvent):
      if event.skill == "Pass":
        assert self.is_same_player(event) and not self.reroll
        self.reroll = event
        return ChainEvent.CHAINED

    # check for second attempt
    if isinstance(event, ThrowTeamMateEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    # check for landing
    if isinstance(event, LandEvent):
      self.landing = event
      return ChainEvent.CHAINED

    # chain to landing if we've seen it
    if self.landing:
      return self.landing.chain(event)

    return super(ThrowTeamMateEvent, self).chain(event)

class WildAnimalEvent(RerollChainEvent, ActionEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(WildAnimalEvent, self).__init__(desc, team_code, player_code)

    self.result = desc.endswith("Success")

  def get_string_template(self, detail=True):
    if detail and not self.result:
      return Template("${%s} is a wild animal" % self.get_code())

  @staticmethod
  def check(desc):
    return " Wild Animal  (4+) :" in desc

  def chain(self, event):
    # check for second attempt
    if isinstance(event, WildAnimalEvent) and self.is_rerolling():
      assert self.is_same_player(event)
      self.rerolled = True
      self.result = event.result
      return ChainEvent.CHAINED

    return super(WildAnimalEvent, self).chain(event)

# 03 Code
#--------------------------------------------------------------------#

class KickOffEvent(InjuryChainEvent):
  def __init__(self, desc):
    super(KickOffEvent, self).__init__(desc)
    self.result = desc.split(".")[-1].strip()
    self.weather = None

  def get_string_template(self, detail=True):
    if self.result == "Throw a Rock":
      if self.injury_check:
        return Template("${%s} gets a rock thrown at ${%s_ogp} and %s!" % (
          self.get_code(), self.get_code(), self.get_injury_name()))

    if self.result == "Changing Weather":
      return Template("The weather changes to ${%s}" % (
        self.get_code(), self.get_code(), self.get_injury_name()))

    if detail:
      return Template("Kick-off table result: %s" % self.result)

  @staticmethod
  def check(desc):
    return desc.startswith("Kick-Off Table:")

  def chain(self, event):
    if isinstance(event, CasualtyCheckEvent):
      if self.result == "Throw a Rock":
        self.injury_check = Event()
        self.injury_check.team_code = event.team_code
        self.injury_check.player_code = event.player_code
        self.injury_check.result = InjuryCheckEvent.CASUALTY

    if isinstance(event, WeatherEvent):
      if self.result == "Changing Weather":
        self.weather = event.weather
        return ChainEvent.CHAINED

    if isinstance(event, CatchEvent):
      return ChainEvent.END

    if isinstance(event, SkillEvent):
      if event.skill == "Stunty":
        # Don't know what's going on here but stunty seems to end things
        return ChainEvent.CHAINED

    if isinstance(event, PlayerCountEvent):
      return ChainEvent.END

    return super(KickOffEvent, self).chain(event)

# 04 Code
#--------------------------------------------------------------------#

class HalfTimeEvent(Event):
  def __init__(self, desc):
    super(HalfTimeEvent, self).__init__(desc)

  def get_string_template(self, detail=True):
    return Template("Half time!")

  @staticmethod
  def check(desc):
    return desc.startswith("Half Time")

# 05 Code
#--------------------------------------------------------------------#

class PlayerCountEvent(Event):
  def __init__(self, desc):
    team_code, self.player_count = (desc[0:3], int(desc[9:11]))
    super(PlayerCountEvent, self).__init__(desc, team_code)

  def get_string_template(self, detail=True):
    return Template("${%s} field %s players" % (
      self.get_code(), self.player_count))

  @staticmethod
  def check(desc):
    return "players on the pitch" in desc

# 06 Code
#--------------------------------------------------------------------#

class MatchStartEvent(Event):
  def __init__(self, desc):
    self.team_code_map = {}
    for team_string in [x.strip() for x in desc.split(" vs ")]:
      team, team_code = (team_string[:-5], team_string[-4:-1])
      self.team_code_map[team_code] = team
    super(MatchStartEvent, self).__init__(desc)

  def get_string_template(self, detail=True):
    return Template(" vs. ".join(self.team_code_map.values()))

  @staticmethod
  def check(desc):
    return " vs " in desc

class CoinTossEvent(Event):
  def __init__(self, desc):
    team_code, self.choice = desc.split(" choose ")
    super(CoinTossEvent, self).__init__(desc, team_code)

  def get_string_template(self, detail=True):
    return Template("${%s} choose %s on the toss" % (
      self.get_code(), self.choice))

  @staticmethod
  def check(desc):
    return " choose " in desc and not " to " in desc

class CoinOutcomeEvent(Event):
  def __init__(self, desc):
    team_code, self.choice = desc.split(" choose to ")
    super(CoinOutcomeEvent, self).__init__(desc, team_code)

  def get_string_template(self, detail=True):
    return Template("${%s} win the toss and choose to %s" % (
      self.get_code(), self.choice.lower()))

  @staticmethod
  def check(desc):
    return " choose to " in desc

class WeatherEvent(Event):
  def __init__(self, desc):
    self.weather = desc.split(". ")[-1]
    super(WeatherEvent, self).__init__(desc)

  def get_string_template(self, detail=True):
    return Template("The weather at the kick-off is %s" %
        self.weather.lower())

  @staticmethod
  def check(desc):
    return desc.startswith("Weather Table:")

class MatchEndEvent(Event):
  @staticmethod
  def check(desc):
    return desc == "Game Over"

  def get_string_template(self, detail=True):
    return Template("The final whistle blows!")

# 07 Code
#--------------------------------------------------------------------#

class TouchdownEvent(ActionEvent):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[18:20])
    super(TouchdownEvent, self).__init__(desc, team_code, player_code)

  def get_string_template(self, detail=True):
    return Template("${%s} scores a touchdown!" % self.get_code())

  @staticmethod
  def check(desc):
    return " Touchdown " in desc

# 08 Code
#--------------------------------------------------------------------#

class BribeEvent(Event):
  def __init__(self, desc):
    team_code = desc[0:3]
    super(BribeEvent, self).__init__(desc, team_code)

  @staticmethod
  def check(desc):
    return "use a Bribe" in desc

class RerollEvent(Event):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[23:25])
    super(RerollEvent, self).__init__(desc, team_code, player_code)

  @staticmethod
  def check(desc):
    return "use a re-roll" in desc

# 11 Code
#--------------------------------------------------------------------#

class SkillEvent(Event):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(SkillEvent, self).__init__(desc, team_code, player_code)
    self.skill = desc.split(" uses ")[1][:-1].strip()

  def get_string_template(self, detail=True):
    if self.detail:
      return Template("${%s} uses %s" % (self.get_code(), self.skill.lower()))

  @staticmethod
  def check(desc):
    return " uses " in desc

# 12 Code
#--------------------------------------------------------------------#

class SPPEvent(Event):
  def __init__(self, desc):
    team_code, player_code = (desc[0:3], desc[5:7])
    super(SPPEvent, self).__init__(desc, team_code, player_code)

    self.achievement = desc.split("(")[1][:-1].strip()

  @staticmethod
  def check(desc):
    return " SPP " in desc

# 13 Code
#--------------------------------------------------------------------#

class ApothEvent(Event):
  def __init__(self, desc):
    team_code = desc[0:3]
    super(ApothEvent, self).__init__(desc, team_code)

  @staticmethod
  def check(desc):
    return " call on their Apothecary " in desc

class ApothHealEvent(IgnoredEvent):
  @staticmethod
  def check(desc):
    return " Apothecary heals " in desc


